﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace RadowCore.Models
{    
    public interface IPropagationGraphReopository: IEnumerator
    {
        /// <summary>
        /// Stores a propagation graph in the database
        /// </summary>
        /// <param name="propagationGraphRoot">an object representing the graph root</param>
        /// <returns>true if success, else returns false</returns>
        //int Store(PGNode propagationGraphRoot, string hashValue);
        int Store(List<PGNode> nodeList, PGNode rootNode, string rootHashValue);
        /// <summary>
        /// Loads a propagation graph from the database
        /// </summary>
        /// <param name="propagationGraphRoot">an object representing the graph root</param>
        /// <returns>true if success, else returns false</returns>
        PGNode loadGraph(PGNode_DB PGId);


        PGNode SelectGraph(int rootNodeId);
        bool ClearReposity();
    }

    class Edge
    {
        public string edgeLabel { set; get; }
        public PGNode_DB destinationNode { set; get; }
    }
    class NodeEdgeNode
    {
        public string sourceNodeName { get; set; }
        public DateTime sourceNodeTimeStamp { get; set; }
        public string sourceNodeOriginator { get; set; }
        public string sourceNodeRole { get; set; }
        public string destinationNodeName { get; set; }
        public DateTime destinationNodeTimeStamp { get; set; }
        public string destinationNodeOriginator { get; set; }
        public string destinationNodeRole { get; set; }
        public string valueLabel { get; set; }
    }
    public class PropagationGraphDBRepository : IPropagationGraphReopository
    {

        /// <summary>
        /// an index keeps track of the current
        /// </summary>
        private int position =  -1;
        RadowDataClassesDataContext _db;
        
        public PropagationGraphDBRepository()
        {
            this.position = -1;
            this._db = new RadowDataClassesDataContext();
        }
        public bool MoveNext()
        {
            position++;
            return (position < _db.PropagationGraph_DBs.Count());
        }
        public void Reset()
        {
            position = -1;
        }
        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }
        public PGNode Current
        {
            get
            {
                
                int pgId = _db.PropagationGraph_DBs.ToList().ElementAt(position).PropagationGraphId;
                return SelectGraph(pgId);                
            }
        }

        public bool ClearReposity()
        {
            try
            {
                var relations = _db.PGRelation_DBs.ToList();
                var nodes = _db.PGNode_DBs.ToList();
                var pgs = _db.PropagationGraph_DBs.ToList();
                _db.PGRelation_DBs.DeleteAllOnSubmit(relations);
                _db.PGNode_DBs.DeleteAllOnSubmit(nodes);
                _db.PropagationGraph_DBs.DeleteAllOnSubmit(pgs);
                _db.SubmitChanges();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }

        }

        public PGNode loadGraph(PGNode_DB rootNode)
        {
           
            PGNode node = new PGNode
            {
                Name = rootNode.Name,
                Orignator = rootNode.Originator
            };

            var destinationNodes = from relations in _db.PGRelation_DBs
                                   join nodes in _db.PGNode_DBs on relations.DestinationNodeId equals nodes.PGNodeId
                                   where (relations.SourceNodeId == rootNode.PGNodeId)
                                   select new Edge
                                   {
                                       destinationNode = nodes,
                                       edgeLabel = relations.ValueLabel
                                   };
            node.Edges = new List<PGEdge>();
            foreach (Edge n in destinationNodes)
            {
                PGEdge edge = new PGEdge();
                edge.Value = n.edgeLabel;
                edge.TargetNode = loadGraph(n.destinationNode);
                node.Edges.Add(edge);
            }
            return node;
        }

        public int Store(List<PGNode> nodeList, PGNode rootNode, string rootHashValue)
        {
            //storing an entry for the propagation graph contains the graph hash value:
            PropagationGraph_DB propagationGraph = new PropagationGraph_DB { HashValue = rootHashValue};
            _db.PropagationGraph_DBs.InsertOnSubmit(propagationGraph);
            _db.SubmitChanges();

            //keep the propagation graph entry id:
            int propagationGraphId = propagationGraph.PropagationGraphId;

            // go through all the nodes in nodeList and store them in the tables:
            foreach (PGNode node in nodeList)
            {
                PGNode_DB dbNode = new PGNode_DB();
                dbNode.Name = node.Name;
                dbNode.Originator = node.Orignator;
                dbNode.EventTimestamp = node.EventTimeStamp;
                dbNode.PropagationGraphId = propagationGraphId;
                dbNode.Role = node.Role;
                _db.PGNode_DBs.InsertOnSubmit(dbNode);
            }
            _db.SubmitChanges();

            var nodes = _db.PGNode_DBs.Where(N => N.PropagationGraphId == propagationGraphId);
            //store all node's edges
            foreach (PGNode node in nodeList)
            {
                if (node.Edges.Count > 0)
                {
                    PGNode_DB sourceNode = nodes.Where(n => n.Name == node.Name).First();
                    foreach (PGEdge edge in node.Edges)
                    {
                        PGNode_DB destinationNode = nodes.Where(n => n.Name == edge.TargetNode.Name).First(); 
                        PGRelation_DB rela = new PGRelation_DB
                        {
                            SourceNodeId = sourceNode.PGNodeId,
                            DestinationNodeId = destinationNode.PGNodeId,
                            ValueLabel = edge.Value
                        };
                        //insert the entry into the db:
                        _db.PGRelation_DBs.InsertOnSubmit(rela);
                    }
                }
                
            }
            _db.SubmitChanges();
            return propagationGraphId; ;
        }


 
        public PGNode SelectGraph(int pgId)
        {
            //PGNode_DB pgRootDb = _db.PropagationGraph_DBs.FirstOrDefault(g => g.PropagationGraphId == pgId).PGNode_DB;
            //return loadGraph(pgRootDb);
            Dictionary<string, PGNode> allNodes = new Dictionary<string, PGNode>();
            PGNode rootNode = (from n in _db.PGNode_DBs
                           where n.PropagationGraphId == pgId && !(from e in _db.PGRelation_DBs
                                   where e.PGNode_DB.PropagationGraphId == pgId && e.PGNode_DB1.PropagationGraphId == pgId
                                   select e.DestinationNodeId).Contains(n.PGNodeId)
                           select new PGNode{
                            Name = n.Name,
                            Orignator = n.Originator,
                            EventTimeStamp = n.EventTimestamp,
                            Role = n.Role,
                            Edges = new List<PGEdge>()
                           }).FirstOrDefault<PGNode>();
            allNodes.Add(rootNode.Name, rootNode);
            
            var nodes = from n1 in _db.PGNode_DBs 
                        join e in _db.PGRelation_DBs on n1.PGNodeId equals e.SourceNodeId
                        join n2 in _db.PGNode_DBs on e.DestinationNodeId equals n2.PGNodeId
                        where n1.PropagationGraphId == pgId && n2.PropagationGraphId == pgId
                        select new NodeEdgeNode
                        {
                             sourceNodeName = n1.Name,
                             sourceNodeTimeStamp = n1.EventTimestamp,
                             sourceNodeOriginator = n1.Originator,
                             sourceNodeRole = n1.Role,
                             destinationNodeName = n2.Name,
                             destinationNodeTimeStamp = n2.EventTimestamp,
                             destinationNodeOriginator = n2.Originator,
                             destinationNodeRole = n2.Role,
                             valueLabel = e.ValueLabel                             
                        };
            foreach (NodeEdgeNode tuple in nodes)
            {
                PGNode sourceNode = new PGNode();
                PGNode destinationNode = new PGNode();
                if (allNodes.ContainsKey(tuple.sourceNodeName))
                    sourceNode = allNodes[tuple.sourceNodeName];
                else
                {
                    sourceNode = new PGNode
                    {
                        Name = tuple.sourceNodeName,
                        EventTimeStamp = tuple.sourceNodeTimeStamp,
                        Orignator = tuple.sourceNodeOriginator,
                        Role = tuple.sourceNodeRole,
                        Edges =  new List<PGEdge>()
                    };
                    allNodes.Add(sourceNode.Name, sourceNode);
                }
                if (allNodes.ContainsKey(tuple.destinationNodeName))
                    destinationNode = allNodes[tuple.destinationNodeName];
                else
                {
                    destinationNode = new PGNode
                    {
                        Name = tuple.destinationNodeName,
                        EventTimeStamp = tuple.destinationNodeTimeStamp,
                        Orignator = tuple.destinationNodeOriginator,
                        Role = tuple.destinationNodeRole,
                        Edges = new List<PGEdge>()
                    };
                    allNodes.Add(destinationNode.Name, destinationNode);
                }
                PGEdge edge = new PGEdge
                {
                    TargetNode = destinationNode,
                    Value = tuple.valueLabel
                };
                sourceNode.Edges.Add(edge);                                    
            }
            return rootNode;
        }
    
    
    }

    public class PropagationGraphMemoryRepository : IPropagationGraphReopository
    {

        /// <summary>
        /// an index keeps track of the current
        /// </summary>
        private int position = -1;
        /// <summary>
        /// a hash table contains all the propagation graphs, key is pg hash key, value is pgnode (root node in the pg)
        /// </summary>
        Dictionary<string, PGNode> _repository;

        public PropagationGraphMemoryRepository()
        {
            this.position = -1;         
        }
        public bool MoveNext()
        {
            position++;
            return (position < _repository.Count());
        }
        public void Reset()
        {
            position = -1;
        }
        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }
        public PGNode Current
        {
            get
            {

                return _repository.ToList().ElementAt(position).Value;                
            }
        }

        public bool ClearReposity()
        {
            try
            {
                _repository.Clear();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }

        }

        public PGNode loadGraph(PGNode_DB rootNode)
        {

            PGNode node = new PGNode
            {
                Name = rootNode.Name,
                Orignator = rootNode.Originator
            };
            return node;
        }

        public int Store(List<PGNode> nodeList, PGNode rootNode, string rootHashValue)
        {
            _repository.Add(rootHashValue, rootNode);
            return 0;
        }



        public PGNode SelectGraph(int pgId)
        {            
            return _repository[pgId.ToString()];
        }
    }
}
